{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "829d566a",
   "metadata": {
    "language_info": {
     "name": "polyglot-notebook"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "该类型应用了 MyAttribute 特性\n",
      "\n",
      "测试特性使用的类\n",
      "特性的方法\n"
     ]
    }
   ],
   "source": [
    "// 特性是一种允许我们向程序的程序集添加元数据的语言结构\n",
    "// 它是用于保存程序结构信息的某种特殊类型的类\n",
    "\n",
    "// 特性提供方法以将声明信息与C#代码(类型,方法,属性等)相关联\n",
    "// 特性与程序实体关联后,即可在运行时使用反射查询特性信息\n",
    "\n",
    "// 特性的目的是告诉编译器把程序结构的某组元数据嵌入程序集中\n",
    "// 它可以放置在几乎所有的声明中(类,变量,函数等等申明)\n",
    "\n",
    "/**\n",
    "    特性本质是个类\n",
    "    我们可以利用特性类为元数据添加额外的信息\n",
    "        eg: 类,成员变量,成员方法等等为他们添加更多的额外信息\n",
    "    之后可以通过反射来获取这些额外信息\n",
    "*/\n",
    "\n",
    "// 自定义特性 继承特性基类\n",
    "class MyAttribute : Attribute {\n",
    "    //特性中的成员,一般根据需求写\n",
    "    public string info;\n",
    "\n",
    "    public MyAttribute(string info) {\n",
    "        this.info = info;\n",
    "    }\n",
    "\n",
    "    public void TestFuc() {\n",
    "        Console.WriteLine(\"特性的方法\");\n",
    "    }\n",
    "}\n",
    "\n",
    "// 特性的使用\n",
    "// [特性名(参数列表)] 本质上就是在调用特性类的构造函数, 写在类,函数,变量上一行,表示他们具有该特性信息\n",
    "// 特姓名会自动加上Attribute,不需要写出\n",
    "[My(\"测试特性使用的类\")]\n",
    "class Test {\n",
    "    [My(\"用于测试特性使用的方法\")]\n",
    "    public void TestFuc([My(\"函数参数\")]int a) {\n",
    "\n",
    "    }\n",
    "}\n",
    "\n",
    "\n",
    "Test test = new Test();\n",
    "Type type_test = test.GetType();\n",
    "\n",
    "//判断是否使用了某个特性,false代表不查找继承链(属性和事件忽略此参数)\n",
    "if(type_test.IsDefined(typeof(MyAttribute),false)) {\n",
    "    Console.WriteLine(\"该类型应用了 MyAttribute 特性\");\n",
    "\n",
    "    //获取Type元数据的所有特性\n",
    "    object[] array = type_test.GetCustomAttributes(true); //判断继承链\n",
    "    for(int i=0; i < array.Length; i++) {\n",
    "        if(array[i] is MyAttribute) {\n",
    "            MyAttribute attribute = array[i] as MyAttribute;\n",
    "            Console.WriteLine(\"\\n\" + attribute.info);\n",
    "\n",
    "            attribute.TestFuc();\n",
    "        }\n",
    "    }\n",
    "}\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "language_info": {
     "name": "polyglot-notebook"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    }
   },
   "outputs": [],
   "source": [
    "// 限制自定义特性的使用范围\n",
    "// 通过为特性类加特性限制其使用范围\n",
    "// 参数一: AttributeTargets -特性可以使用在那些地方\n",
    "// 参数二: AllowMultiple -是否允许多个特性用例作用在同一个目标上\n",
    "// 参数三: Inherited -特性能否被派生类和重写成员继承\n",
    "[AttributeUsage(AttributeTargets.Class | AttributeTargets.Field, AllowMultiple = true, Inherited = true)]\n",
    "public class MyCustomAttribute :Attribute {\n",
    "    public string info;\n",
    "\n",
    "    public MyCustomAttribute(string info) {\n",
    "        this.info = info;\n",
    "    }\n",
    "\n",
    "    public void TestFuc() {\n",
    "        Console.WriteLine(\"特性的方法\");\n",
    "    }\n",
    "}\n",
    "\n",
    "[MyCustom(\" 限制自定义特性的使用类 \")]\n",
    "[MyCustom(\" AllowMultiple = true\")]\n",
    "class Test {\n",
    "    [MyCustom(\" 成员变量 \")]\n",
    "    public int value = 0;\n",
    "\n",
    "    // [MyCustom(\" 函数的特性报错 \")]\n",
    "    public void TestFuc() {\n",
    "\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "language_info": {
     "name": "polyglot-notebook"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "测试调用者信息\n",
      "\n",
      "42\n",
      "<Initialize>\n",
      "Test 执行\n"
     ]
    }
   ],
   "source": [
    "#define Test\n",
    "using System.Runtime.CompilerServices;\n",
    "using System.Diagnostics;\n",
    "using System.Runtime.InteropServices;\n",
    "// 系统自带特性\n",
    "// 1. 过时特性Obsolete\n",
    "class TestClass {\n",
    "    //参数二: true-使用该方法时会报错,false使用时会警告\n",
    "    [Obsolete(\"OldSpeak 方法过时,请使用Speak方法\",true)]\n",
    "    public void OldSpeak(string str){\n",
    "\n",
    "    }\n",
    "\n",
    "    public void SpeakCaller(string str,\n",
    "    [CallerFilePath]\n",
    "    string fileName = \"\",\n",
    "    [CallerLineNumber]\n",
    "    int line = 0, \n",
    "    [CallerMemberName]\n",
    "    string target = \"\") {\n",
    "        //使用默认值,特性标签会自动给默认值赋值\n",
    "        Console.WriteLine(str + \"\\n\" + fileName + \"\\n\" + line + \"\\n\" + target);\n",
    "    }\n",
    "\n",
    "    [Conditional(\"Test\")]\n",
    "    public void Test() {\n",
    "        Console.WriteLine(\"Test 执行\");\n",
    "    }\n",
    "\n",
    "    [DllImport(\"Test.dll\")]\n",
    "    public static extern int Add(int a, int b); //固定写法将Test.dll的方法映射到C#的函数上\n",
    "} \n",
    "\n",
    "TestClass testClass = new TestClass();\n",
    "// testClass.OldSpeak(\"Hello\");\n",
    "\n",
    "// 2. 调用者信息特性 using System.Runtime.CompilerServices;\n",
    "//       - CallerFilePath: 那个文件调用\n",
    "//       - CallerLineNumber: 那一行调用\n",
    "//       - CallerMemberName: 那个函数调用\n",
    "// 打印错误信息\n",
    "testClass.SpeakCaller(\"测试调用者信息\");\n",
    "\n",
    "// 3. 条件编译特性 Conditional: 它会和预处理指令#define配合使用\n",
    "//       - 需要引用命名空间 using System.Diagnostics;\n",
    "//       - 主要可以使用在一些调试的代码上\n",
    "testClass.Test();\n",
    "\n",
    "// 4. DllImport: 用来标记非.NET(C#)的函数,表明这个函数在外部的DLL定义,一般用来调用C,C++的DLL包写好的方法\n",
    "//       - 需要引用命名空间using System.Runtime.InteropServices;\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".NET (C#)",
   "language": "C#",
   "name": ".net-csharp"
  },
  "language_info": {
   "name": "polyglot-notebook"
  },
  "polyglot_notebook": {
   "kernelInfo": {
    "defaultKernelName": "csharp",
    "items": [
     {
      "aliases": [],
      "name": "csharp"
     }
    ]
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
